/******************************************************
* sensor_ov5150_csi.c
*
* Purpose: tvp5150 sensor driver for CSI path
*
* Author: Eugene Hsu
*
* Date: 2014/08/25
*
* Copyright Generalplus Corp. ALL RIGHTS RESERVED.
*
* Version :
* History :
*
*******************************************************/
/*******************************************************
    Include file
*******************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "drv_l1_sfr.h"
#include "drv_l1_csi.h"
#include "drv_l1_i2c.h"
#include "drv_l2_sensor.h"
#include "drv_l2_sccb.h"
#include "gplib_print_string.h"
#include "gplib_aeawb.h"

#if ((defined _TVIN_TVP5150_CSI) && (_TVIN_TVP5150_CSI == 1)) || ((defined _TVIN_GM7150_CSI) && (_TVIN_GM7150_CSI == 1)|| (defined _TVIN_VC0705_CSI) && (_TVIN_VC0705_CSI == 1) )

/**************************************************************************
 *                           C O N S T A N T S                            *
 **************************************************************************/
#define TVP5150_SLAVE_ID_SEL	0

#if TVP5150_SLAVE_ID_SEL
#define TVP5150_ID	0xB8
#else
#define TVP5150_ID	0xBA
#endif

#define VC0705_ID   0xAA // IF CPU_SADD0 PULL HIHG , ELSE  0XA8

#define TVP5150_WIDTH		688
#define TVP5150_HEIGHT		240

#define TVP5150_OUT_WIDTH	320
#define TVP5150_OUT_HEIGHT	240

*/
/**************************************************************************
 *                          D A T A    T Y P E S                          *
 **************************************************************************/
typedef struct regval8_s
{
	INT8U	reg_num;
	INT8U	value;
} regval8_t;

/**************************************************************************
 *                         G L O B A L    D A T A                         *
 **************************************************************************/
#if SENSOR_I2C_MODE == SENSOR_I2C_MODE_SW
sccb_config_t tvp5150_handle =
{
	SENSOR_I2C_SCL,		//scl_port
	SENSOR_I2C_DRIVING,	//scl_drv
	SENSOR_I2C_SDA,		//sda_port
	SENSOR_I2C_DRIVING,	//sda_drv
	0,			//pwdn_port
	0,			//pwdn_drv
	0,			//have_pwdn

	#if ((defined _TVIN_TVP5150_CSI) && (_TVIN_TVP5150_CSI == 1))
	8,			//RegBits
	#endif
	#if ((defined _TVIN_VC0705_CSI) && (_TVIN_VC0705_CSI == 1))
	16,
	#endif

	8,			//DataBits

	#if ((defined _TVIN_TVP5150_CSI) && (_TVIN_TVP5150_CSI == 1))
	TVP5150_ID, //slaveAddr
	#endif
	#if ((defined _TVIN_VC0705_CSI) && (_TVIN_VC0705_CSI == 1))
	VC0705_ID, //slaveAddr
	#endif

	0x20000,	//timeout
	100			//clock_rate
};
#elif SENSOR_I2C_MODE == SENSOR_I2C_MODE_HW
static drv_l1_i2c_bus_handle_t	tvp5150_handle =
{
	SENSOR_I2C_DEV,

	#if ((defined _TVIN_TVP5150_CSI) && (_TVIN_TVP5150_CSI == 1))
	TVP5150_ID, //slaveAddr
	#endif
	#if ((defined _TVIN_VC0705_CSI) && (_TVIN_VC0705_CSI == 1))
	VC0705_ID, //slaveAddr
	#endif

	100
};
#endif

static void tvp5150_sccb_open(void)
{
	#if SENSOR_I2C_MODE == SENSOR_I2C_MODE_HW
	drv_l1_i2c_init(&tvp5150_handle);
	#endif
}

static void tvp5150_sccb_close(void)
{
	#if SENSOR_I2C_MODE == SENSOR_I2C_MODE_HW
	drv_l1_i2c_uninit(&tvp5150_handle);
	#endif
}


static INT32S vc0705_sccb_write(INT16U reg, INT8U value)
{
	#if SENSOR_I2C_MODE == SENSOR_I2C_MODE_SW
	return drv_l2_sccb_write(&tvp5150_handle, reg, value);
	#elif SENSOR_I2C_MODE == SENSOR_I2C_MODE_HW
	return drv_l1_reg_2byte_data_1byte_write(&tvp5150_handle, reg, value);
	#endif
}


static INT32S tvp5150_sccb_write(INT8U reg, INT8U value)
{
	#if SENSOR_I2C_MODE == SENSOR_I2C_MODE_SW
	return drv_l2_sccb_write(&tvp5150_handle, reg, value);
	#elif SENSOR_I2C_MODE == SENSOR_I2C_MODE_HW
	return drv_l1_reg_1byte_data_1byte_write(&tvp5150_handle, reg, value);
	#endif
}

static INT32S tvp5150_sccb_read(INT16U reg)
{
#if SENSOR_I2C_MODE == SENSOR_I2C_MODE_SW
	INT16U data;
	INT16U stub, *value = &stub;

	if(drv_l2_sccb_read(&tvp5150_handle, reg, &data) >= 0) {
		*value = data;
		return STATUS_OK;
	} else {
		*value = 0xFF;
		return STATUS_FAIL;
	}
#elif SENSOR_I2C_MODE == SENSOR_I2C_MODE_HW
	INT8U data[1];

	data[0] = reg;
	if(drv_l1_i2c_bus_write(&tvp5150_handle, data, 1) < 0) {
		*value = 0xFF;
		return STATUS_FAIL;
	}

	if(drv_l1_i2c_bus_read(&tvp5150_handle, data, 1) < 0) {
		*value = 0xFF;
		return STATUS_FAIL;
	}

	*value = data[0];
#endif
}


static INT32S vc0705_init(void)
{
	tvp5150_sccb_read(0x01C2);

	if(tvp5150_sccb_read(0x01C3)==0x7)
	{
		// config simlation
		vc0705_sccb_write(0x01b9 , 0x15);

		vc0705_sccb_write(0x01b8 , 0xc7);

		vc0705_sccb_write(0x01b7 , 0xc8);
		vc0705_sccb_write(0x01ba , 0x01);

		vc0705_sccb_write(0x01b9 , 0x27);
		vc0705_sccb_write(0x01b8 , 0x41);
		vc0705_sccb_write(0x01b7 , 0xc8);
		vc0705_sccb_write(0x01ba , 0x01);

		vc0705_sccb_write(0x01b9 , 0x29);
		vc0705_sccb_write(0x01b8 , 0xb3);
		vc0705_sccb_write(0x01b7 , 0xc8);
		vc0705_sccb_write(0x01ba , 0x01);

		vc0705_sccb_write(0x01b9 , 0x2a);
		vc0705_sccb_write(0x01b8 , 0xbb);
		vc0705_sccb_write(0x01b7 , 0xc8);
		vc0705_sccb_write(0x01ba , 0x01);

		vc0705_sccb_write(0x01b9 , 0x3d);
		vc0705_sccb_write(0x01b8 , 0x21);
		vc0705_sccb_write(0x01b7 , 0xc8);
		vc0705_sccb_write(0x01ba , 0x01);

		vc0705_sccb_write(0x01b9 , 0x3e);
		vc0705_sccb_write(0x01b8 , 0x2b);
		vc0705_sccb_write(0x01b7 , 0xc8);
		vc0705_sccb_write(0x01ba , 0x01);

		vc0705_sccb_write(0x01a3 , 0x37);
		vc0705_sccb_write(0x01b9 , 0x64);
		vc0705_sccb_write(0x01b8 , 0x54);
		vc0705_sccb_write(0x01b7 , 0x4c);
		vc0705_sccb_write(0x01ba , 0x3c);
		vc0705_sccb_write(0x01a3 , 0x36);
	}

	// close LIM
	vc0705_sccb_write(0x01a4 , 0x1d);
	// CAL CFE
	vc0705_sccb_write(0x01aa , 0xf4);
	vc0705_sccb_write(0x01a3 , 0x37);
	vc0705_sccb_write(0x01aa , 0x00);
	vc0705_sccb_write(0x01be , 0x40);
	vc0705_sccb_write(0x01bf , 0x87);
	vc0705_sccb_write(0x01a3 , 0x36);
	// low power comsumption hanlde
	vc0705_sccb_write(0x01a0 , 0x8c);
	vc0705_sccb_write(0x01a2 , 0x68);
	vc0705_sccb_write(0x01ad , 0x57);
	vc0705_sccb_write(0x01b6 , 0x0c);
	vc0705_sccb_write(0x01cb , 0x50);

	vc0705_sccb_write(0x01ac , 0x75);
	vc0705_sccb_write(0x00e5 , 0x0f);
	vc0705_sccb_write(0x00e6 , 0xff);
	vc0705_sccb_write(0x00e7 , 0x7d);
	vc0705_sccb_write(0x00eb , 0x00);
	vc0705_sccb_write(0x00ef , 0x00);
	vc0705_sccb_write(0x00f3 , 0x80);

	vc0705_sccb_write(0x01ca , 0x01);
	vc0705_sccb_write(0x01dc , 0x00);
	// close output clk
	vc0705_sccb_write(0x01cc , 0x99);
	vc0705_sccb_write(0x01cd , 0x99);
	vc0705_sccb_write(0x01ce , 0x99);
	vc0705_sccb_write(0x01cf , 0x99);
	//GPIO5 used as irq
	vc0705_sccb_write(0x41c6 , 0x10);
	//GPIO 12 set to constant 0
	vc0705_sccb_write(0x00b5 , 0x66);
	vc0705_sccb_write(0x00b6 , 0x66);
	//VDO2 output Vin4
	vc0705_sccb_write(0x00b2 , 0xec);
	//
	vc0705_sccb_write(0x01c7 , 0x48);
	// enable output of ch2 and MISC
	vc0705_sccb_write(0x01d9 , 0x1a);
	//setting VCLKPO4
	vc0705_sccb_write(0x01cf , 0x81);
	// open internal clk
	vc0705_sccb_write(0x01c7 , 0x58);
}

static INT32S tvp5150_init(void)
{
#if ((defined _TVIN_TVP5150_CSI) && (_TVIN_TVP5150_CSI == 1))
	tvp5150_sccb_write(0x05, 0x01);
	drv_tiny_wait(200);

	tvp5150_sccb_read(0x05);

	tvp5150_sccb_write(0x05, 0x00);

	//osDelay(200);
	tvp5150_sccb_read(0x05);

	tvp5150_sccb_write(0x02, 0x00);

	//osDelay(200);
	tvp5150_sccb_read(0x02);

	tvp5150_sccb_write(0xD0, 0xFF);

	//osDelay(200);
	tvp5150_sccb_read(0xD0);

	tvp5150_sccb_write(0x0f, 0x0a);

	//osDelay(200);
	tvp5150_sccb_read(0x0f);

	tvp5150_sccb_write(0x03, 0x6d);

	//osDelay(200);
	tvp5150_sccb_read(0x03);
	return tvp5150_sccb_write(0x0d, 0x47);
#elif ((defined _TVIN_GM7150_CSI) && (_TVIN_GM7150_CSI == 1))
	tvp5150_sccb_write(0x03, 0x0d);
	tvp5150_sccb_write(0xa0, 0x55);
	tvp5150_sccb_write(0xa1, 0xaa);
	tvp5150_sccb_write(0x76, 0x40);
	tvp5150_sccb_write(0x77, 0xf0);
#endif
}

/**
 * @brief   tvp5150 initialization function
 * @param   sensor format parameters
 * @return 	none
 */
void tvp5150_csi_init(void)
{
	drv_l1_csi_init(CSI_DEV_NUM);

	/* mclk output */
	drv_l2_sensor_set_mclkout(tvp5150_csi_ops.info[0].mclk, MASTER_CLOCK_MIPI_KEEP, MASTER_CLOCK_CSI_ON);

	/* request SCCB */
	tvp5150_sccb_open();
	DBG_PRINT("Sensor tvp5150 csi interface init completed\r\n");
}

/**
 * @brief   tvp5150 un-initialization function
 * @param   sensor format parameters
 * @return 	none
 */
void tvp5150_csi_uninit(void)
{
	// disable mclk
	drv_l2_sensor_set_mclkout(MCLK_NONE, MASTER_CLOCK_MIPI_OFF, MASTER_CLOCK_CSI_OFF);

	drv_l1_csi_stop(CSI_DEV_NUM);

	// release SCCB
	tvp5150_sccb_close();
}

/**
 * @brief   tvp5150 stream start function
 * @param   info index
 *
 * @return 	none
 */
void tvp5150_csi_stream_start(INT32U index, INT32U bufA, INT32U bufB)
{
	INT16U	target_w, target_h, sensor_w, sensor_h;

	DBG_PRINT("%s = %d\r\n", __func__, index);

	target_w = tvp5150_csi_ops.info[index].target_w;
	target_h = tvp5150_csi_ops.info[index].target_h;
	sensor_w = tvp5150_csi_ops.info[index].sensor_w;
	sensor_h = tvp5150_csi_ops.info[index].sensor_h;

	/* enable CSI output clock for SCCB */
	//drv_l2_sensor_set_mclkout(tvp5150_csi_ops.info[index].mclk);
	drv_l1_csi_set_clock(MASTER_CLOCK_24MHZ, MASTER_CLOCK_MIPI_KEEP, MASTER_CLOCK_CSI_ON);

	/* set start frame buffer address */
	if(bufA)
	{
		drv_l1_csi_set_buf(CSI_DEV_NUM, bufA);
	}
	else
	{
		DBG_PRINT("Input frame buffer address error, fail to start\r\n");
		tvp5150_csi_ops.stream_stop();
	}

	drv_l1_csi_input_set(CSI_DEV_NUM, tvp5150_csi_ops.info[index].interface_mode, tvp5150_csi_ops.info[index].interlace_mode, 0, tvp5150_csi_ops.info[index].input_format);

	/* Set Horizontal/Vertical counter reset, Vertical counter incresase selection, sensor owner inerrupt enable, capture/preview mode */
	drv_l1_csi_input_latch_timing1_set(CSI_DEV_NUM, tvp5150_csi_ops.info[index].hoffset, tvp5150_csi_ops.info[index].voffset, 0x02, ENUM_CSI_LATCH_DELAY_3_CLOCK);
	drv_l1_csi_input_latch_timing2_set
	(
		CSI_DEV_NUM,
		tvp5150_csi_ops.info[index].hsync_active,
		tvp5150_csi_ops.info[index].vsync_active,
		tvp5150_csi_ops.info[index].vsync_active,
		tvp5150_csi_ops.info[index].hsync_active
	);

	drv_l1_csi_output_format_set(CSI_DEV_NUM, tvp5150_csi_ops.info[index].output_format);
	drv_l1_csi_output_fifo_set(CSI_DEV_NUM, ENUM_CSI_FIFO_DISABLE);

	/* Set sensor's registers via SCCB */
#if ((defined _TVIN_TVP5150_CSI) && (_TVIN_TVP5150_CSI == 1))
	if(tvp5150_init() < 0)
	{
		DBG_PRINT("tvp5150 init fail!!!\r\n");
	}

#endif
#if ((defined _TVIN_VC0705_CSI) && (_TVIN_VC0705_CSI == 1))
	if(vc0705_init() < 0)
	{
		DBG_PRINT("vc0705 init fail!!!\r\n");
	}
#endif
	drv_l1_csi_input_resolution_set(CSI_DEV_NUM, sensor_w, sensor_h);

	// set scale down
	if((sensor_w > target_w) || (sensor_h > target_h))
	{
		drv_l1_csi_set_scale(CSI_DEV_NUM, sensor_w, target_w, sensor_h, target_h);
	}

	/* csi long burst width need 32 align */
	if(tvp5150_csi_ops.info[index].target_w & 0x1F)
	{
		drv_l1_csi_set_long_burst_mode(CSI_DEV_NUM, 0);
	}
	else
	{
		drv_l1_csi_set_long_burst_mode(CSI_DEV_NUM, 1);
	}

	/* Enable interrupt */
	drv_l1_csi_set_irq(CSI_DEV_NUM, ENABLE);

	drv_l1_csi_start(CSI_DEV_NUM);
}

/**
 * @brief   tvp5150 stream stop function
 * @param   none
 * @return 	none
 */
void tvp5150_csi_stream_stop(void)
{
	/* Disable interrupt */
	drv_l1_csi_set_irq(CSI_DEV_NUM, DISABLE);

	drv_l1_csi_stop(CSI_DEV_NUM);
}

/**
 * @brief   tvp5150 get info function
 * @param   none
 * @return 	pointer to sensor information data
 */
drv_l2_sensor_info_t *tvp5150_csi_get_info(INT32U index)
{
	if(index > (MAX_INFO_NUM - 1))
		return NULL;
	else
		return (drv_l2_sensor_info_t *) &tvp5150_csi_ops.info[index];
}

/*********************************************
*	sensor ops declaration
*********************************************/
const drv_l2_sensor_ops_t tvp5150_csi_ops =
{
	SENSOR_SOURCE_DVP,
	SENSOR_PATH_CSI,
	tvp5150_csi_init,
	tvp5150_csi_uninit,
	tvp5150_csi_stream_start,
	tvp5150_csi_stream_stop,
	NULL,
	NULL,
	NULL,
	tvp5150_csi_get_info,
	{
		/* 1nd info */
		{
			MASTER_CLOCK_24MHZ,		/* CSI clock */
			CSI_IN_YUYV,			/* input format */
			CSI_OUT_YUYV,			/* output format */
			CSI_SENSOR_FPS_30,		/* FPS in sensor */
			688,					/* target width */
			240,					/* target height */
			688,					/* sensor width */
			240,					/* sensor height */
			32,						/* sensor h offset */
			2,						/* sensor v offset */
			CSI_HSYNC_CCIR_656,		/* input interface */
			CSI_NON_INTERLACE,		/* interlace mode */
			ENUM_CSI_FALLING_EDGE,	/* hsync pin active level */
			ENUM_CSI_FALLING_EDGE	/* vsync pin active level */
		}
	}
};
#endif //(defined _SENSOR_TVP5150_CSI) && (_SENSOR_TVP5150_CSI == 1)
